/**
 * @author nttdocomo
 */
define(function(require) {
	require("../dom/helper");
	aries.klass('aries.util.Renderable', aries.Class.extend({

		/**
		 * Allows addition of behavior after rendering is complete. At this stage the Component’s Element
		 * will have been styled according to the configuration, will have had any configured CSS class
		 * names added, and will be in the configured visibility and the configured enable state.
		 *
		 * @template
		 * @protected
		 */
		afterRender : function() {
		},
		/**
		 * Sets references to elements inside the component. This applies {@link #renderSelectors}
		 * as well as {@link #childEls}.
		 * @private
		 */
		applyRenderSelectors : function(el) {
			var me = this, selectors = me.renderSelectors, el = me.$el, dom = el.dom, selector;
			this.applyChildEls(el);
		},
		beforeRender : function() {
			var me = this, target = me.getTargetEl()/*, layout = me.getComponentLayout()*/;

			// Just before rendering, set the frame flag if we are an always-framed component like Window or Tip.
			me.frame = me.frame || me.alwaysFramed;

			/*if (!layout.initialized) {
			layout.initLayout();
			}*/

			// Attempt to set overflow style prior to render if the targetEl can be accessed.
			// If the targetEl does not exist yet, this will take place in finishRender
			if (target) {
				target.css(me.getOverflowStyle() || {});
				me.overflowStyleSet = true;
			}

			me.setUI(me.ui);

			if (me.disabled) {
				// pass silent so the event doesn't fire the first time.
				me.disable(true);
			}
		},

		/**
		 * Handles autoRender.
		 * Floating Components may have an ownerCt. If they are asking to be constrained, constrain them within that
		 * ownerCt, and have their z-index managed locally. Floating Components are always rendered to document.body
		 */
		doAutoRender : function() {
			var me = this;
			if (!me.rendered) {
				if (me.floating) {
					me.doRender(aries.$body);
				} else {
					me.doRender(_.isBoolean(me.autoRender) ? aries.$body : me.autoRender);
				}
			}
		},

		doRenderContent : function(out, renderData) {
			// Careful! This method is bolted on to the renderTpl so all we get for context is
			// the renderData! The "this" pointer is the renderTpl instance!

			var me = renderData.$comp;

			if (me.html) {
				Ext.DomHelper.generateMarkup(me.html, out);
				return me.html;
				delete me.html;
			}

			if (me.tpl) {
				// Make sure this.tpl is an instantiated XTemplate
				if (!me.tpl.isTemplate) {
					me.tpl = new Ext.XTemplate(me.tpl);
				}

				if (me.data) {
					//me.tpl[me.tplWriteMode](target, me.data);
					me.tpl.applyOut(me.data, out);
					delete me.data;
				}
			}
		},

		/**
		 * This method visits the rendered component tree in a "top-down" order. That is, this
		 * code runs on a parent component before running on a child. This method calls the
		 * {@link #onRender} method of each component.
		 * @param {Number} containerIdx The index into the Container items of this Component.
		 *
		 * @private
		 */
		finishRender : function(containerIdx) {

			// Sets the rendered flag and clears the redering flag
			this.onRender(this.container, containerIdx);
			this.afterRender();
		},

		getElConfig : function() {
			var me = this, autoEl = me.autoEl, frameInfo = me.getFrameInfo(), config = {
				tag : 'div',
				id : me.id,
				tpl : frameInfo ? me.initFramingTpl(frameInfo.table) : me.initRenderTpl()
			}, i, frameElNames, len, suffix, frameGenId;

			me.initStyles(this.$el);
			//me.protoEl.writeTo(config);
			//me.protoEl.flush();

			if (_.isString(autoEl)) {
				config.tag = autoEl;
			} else {
				$.extend(config, autoEl);
				// harmless if !autoEl
			}

			if (config.tpl) {
				// Use the framingTpl as the main content creating template. It will call out to this.applyRenderTpl(out, values)
				if (frameInfo) {
					frameElNames = me.frameElNames;
					len = frameElNames.length;
					frameGenId = me.id + '-frame1';

					me.frameGenId = 1;
					config.tplData = Ext.apply({}, {
						$comp : me,
						fgid : frameGenId,
						ui : me.ui,
						uiCls : me.uiCls,
						frameCls : me.frameCls,
						baseCls : me.baseCls,
						frameWidth : frameInfo.maxWidth,
						top : !!frameInfo.top,
						left : !!frameInfo.left,
						right : !!frameInfo.right,
						bottom : !!frameInfo.bottom,
						renderData : me.initRenderData()
					}, me.getFramePositions(frameInfo));

					// Add the childEls for each of the frame elements
					for ( i = 0; i < len; i++) {
						suffix = frameElNames[i];
						me.addChildEls({
							name : 'frame' + suffix,
							id : frameGenId + suffix
						});
					}

					// Panel must have a frameBody
					me.addChildEls({
						name : 'frameBody',
						id : frameGenId + 'MC'
					});
				} else {
					config.tplData = me.initRenderData();
				}
			}

			return config;
		},

		/**
		 * @private
		 * On render, reads an encoded style attribute, "background-position" from the style of this Component's element.
		 * This information is memoized based upon the CSS class name of this Component's element.
		 * Because child Components are rendered as textual HTML as part of the topmost Container, a dummy div is inserted
		 * into the document to receive the document element's CSS class name, and therefore style attributes.
		 */
		getFrameInfo : function() {
			// If native framing can be used, or this component is not going to be framed, then do not attempt to read CSS framing info.
			if ($.support.borderRadius || !this.frame) {
				return false;
			}

			var me = this, frameInfoCache = me.frameInfoCache, el = me.el || me.protoEl, cls = el.dom ? el.dom.className : el.classList.join(' '), frameInfo = frameInfoCache[cls], styleEl, left, top, info;

			if (frameInfo == null) {
				// Get the singleton frame style proxy with our el class name stamped into it.
				styleEl = Ext.fly(me.getStyleProxy(cls), 'frame-style-el');
				left = styleEl.getStyle('background-position-x');
				top = styleEl.getStyle('background-position-y');

				// Some browsers don't support background-position-x and y, so for those
				// browsers let's split background-position into two parts.
				if (!left && !top) {
					info = styleEl.getStyle('background-position').split(' ');
					left = info[0];
					top = info[1];
				}

				frameInfo = me.calculateFrame(left, top);

				if (frameInfo) {
					// Just to be sure we set the background image of the el to none.
					el.setStyle('background-image', 'none');
				}

				//<debug error>
				// This happens when you set frame: true explicitly without using the x-frame mixin in sass.
				// This way IE can't figure out what sizes to use and thus framing can't work.
				if (me.frame === true && !frameInfo) {
					Ext.log.error('You have set frame: true explicity on this component (' + me.getXType() + ') and it ' + 'does not have any framing defined in the CSS template. In this case IE cannot figure out ' + 'what sizes to use and thus framing on this component will be disabled.');
				}
				//</debug>

				frameInfoCache[cls] = frameInfo;
			}

			me.frame = !!frameInfo;
			me.frameSize = frameInfo;

			return frameInfo;
		},

		getRenderTree : function() {
			var me = this;
			//if (!me.hasListeners.beforerender || me.fireEvent('beforerender', me) !== false) {
			me.beforeRender();

			// Flag to let the layout's finishRenderItems and afterFinishRenderItems
			// know which items to process
			me.rendering = true;

			/*if (me.el) {
			 // Since we are producing a render tree, we produce a "proxy el" that will
			 // sit in the rendered DOM precisely where me.el belongs. We replace the
			 // proxy el in the finishRender phase.
			 return {
			 tag : 'div',
			 id : (me.$pid = _.uniqueId())
			 };
			 }*/

			return me.getElConfig();
			//}

			//return null;
		},
		/*html : function() {
		if (this.rendered) {
		return $("<div/>").append(this.$el).html();
		}
		return $("<div/>").append(this.renderEl().$el).html();
		},*/

		/**
		 * Initialized the renderData to be used when rendering the renderTpl.
		 * @return {Object} Object with keys and values that are going to be applied to the renderTpl
		 * @private
		 */
		initRenderData : function() {
			var me = this;

			return this.setupRenderData(_.extend({
				$comp : me,
				id : this.id,
				ui : me.ui,
				uiCls : me.uiCls,
				baseCls : me.baseCls,
				componentCls : me.componentCls,
				frame : me.frame
			}, me.renderData));
		},

		/**
		 * Initializes the renderTpl.
		 * @return {Ext.XTemplate} The renderTpl XTemplate instance.
		 * @private
		 */
		initRenderTpl : function() {
			var tpl = this.getTpl('renderTpl');

			if (tpl && !tpl.renderContent) {
				this.setupRenderTpl(tpl);
			}

			return tpl;
		},
		html : function() {
			this.beforeRender();
			var config = this.getElConfig();
			//tree = this.getRenderTree();
			return config.tpl(config.tplData);
		},
		onBoxReady : aries.emptyFn,
		/*append : function(tree) {
		this.$el.html((new this.renderTpl(this.initRenderData())).html());
		},*/

		/**
		 * Template method called when this Component's DOM structure is created.
		 *
		 * At this point, this Component's (and all descendants') DOM structure *exists* but it has not
		 * been layed out (positioned and sized).
		 *
		 * Subclasses which override this to gain access to the structure at render time should
		 * call the parent class's method before attempting to access any child elements of the Component.
		 *
		 * @param {Ext.core.Element} parentNode The parent Element in which this Component's encapsulating element is contained.
		 * @param {Number} containerIdx The index within the parent Container's child collection of this Component.
		 *
		 * @template
		 * @protected
		 */
		onRender : function(parentNode, containerIdx) {
			this.applyRenderSelectors();
		},
		doRender : function(container, position) {
			var el = this.el, // ensure me.el is wrapped
			tree, nextSibling;

			container = this.initContainer(container);

			nextSibling = this.getInsertPosition(position);
			//if (!el) {
			//tree = this.getRenderTree();
			//this.append(tree)
			// tree will be null if a beforerender listener returns false
			/*if (tree) {
			if (nextSibling) {
			el = aries.DomHelper.insertBefore(nextSibling, tree);
			} else {
			el = aries.DomHelper.append(container, tree);
			}

			me.wrapPrimaryEl(el);
			}*/
			//}

			this.$el.html(this.html());
			//tree.tpl(tree.tplData)
			(container || $(document.body)).append(this.$el);
			//this.delegateEvents();
			//this.$el.addClass(this.classList.join(" "))

			this.finishRender(position);

			this.applyRenderSelectors();
			this.rendered = true;
			this.onBoxReady()
			return this;
		},
		setupRenderData : function(data) {
			return _.extend(data, {
				renderContent : this.doRenderContent
			})
		},
		setupRenderTpl : function(renderTpl) {
			renderTpl.renderBody = renderTpl.renderContent = this.doRenderContent;
		}
	}))
})
